/// <reference path="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.1-vsdoc.js" />
(function ($) {
    var ATTR_HREF = "href",
        ATTR_TITLE = "title",
        ATTR_SRC = "src",
        DOM_BODY = "body",
        BUTTON = 'button',
        CLICK = "click",
        SELECTED_CLASS = "selected",
        OVERLAY_CLASS = "ease-gallery-overlay",
        GALLERY_CLASS = "ease-gallery",
        HTML_DIV = '<div/>',
        HTML_LI = '<li/>',
        HTML_SPACE = '&nbsp;',
        CSS_VISIBILITY = "visibility",
        CSS_MARGIN_LEFT = "margin-left",
        HIDDEN = "hidden",
        VISIBLE = "visible";

    $.fn.easeGallery = function (settings) {
        function UI(options, anchors) {
            var self = this;

            self.Initialized = false;
            self.Anchors = anchors;
            self.Options = options;
            self.Count = anchors.length;
            self.IsMultiple = self.Count > 1;
        }

        //For IDE intellisense
        //        UI.prototype.Initialized = false;
        //        UI.prototype.Anchors = $("a");
        //        UI.prototype.Options = { GalleryName: null, Loop: true, RemoveHref: true, OverlayClose: true, MinWidth: 500, MinHeight: 400, MaxWidth: 1000, MaxHeight: 800 };
        //        UI.prototype.Count = 11;
        //        UI.prototype.IsMultiple = true;
        //        UI.prototype.Data = [];
        //        UI.prototype.SelectedIndex = 0;
        //        UI.prototype.SelectedThumbPage = 0;
        //        UI.prototype.Overlay = $('a');
        //        UI.prototype.Gallery = $('a');
        //        UI.prototype.Header = $('a');
        //        UI.prototype.Loading = $('a');
        //        UI.prototype.Canvas = $('a');
        //        UI.prototype.Pager = $('a');
        //        UI.prototype.Thumb = $('a');
        //        UI.prototype.Nav = $('a');
        //        UI.prototype.Prev = $('a');
        //        UI.prototype.Next = $('a');
        //        UI.prototype.ThumbPrev = $('a');
        //        UI.prototype.ThumbNext = $('a');
        //        UI.prototype.ThumbPages = [{ First: 0, Last: 8, Width: 255}];

        //parses a source anchor element,
        //  and returns single data item related to it.
        UI.prototype.parseAnchor = function (anchor, removeHref) {
            var $anchor = $(anchor),
                href = $anchor.attr(ATTR_HREF);

            removeHref && $anchor.removeAttr(ATTR_HREF);

            return {
                Url: href,
                Desc: $anchor.attr(ATTR_TITLE) || "",
                Thumb: $anchor.children("img:first").clone()
            };
        };

        //preload the image for faster browsering
        UI.prototype.preload = function (src, callback) {
            var img = $(new Image()).load(function () {
                callback && callback(img);

                //clear onLoad, IE behaves irratically with animated gifs otherwise
                img.unbind();
            }).attr(ATTR_SRC, src);
        };

        //Initializes UI properties and DOM elements
        UI.prototype.init = function () {
            var self = this;

            //reset data
            self.Data = [];
            self.SelectedIndex = -1;
            self.SelectedThumbPage = -1;

            //parse source anchors
            $.each(self.Anchors, function () {
                self.Data.push(self.parseAnchor(this, self.Options.RemoveHref));
            });

            //create dom elements
            self.dom().bind().setView().resize();

            //mark as initialized at the end
            self.Initialized = true;
        };

        //creates dom elements needed
        UI.prototype.dom = function () {
            var self = this;

            self.Overlay = $(HTML_DIV).addClass(OVERLAY_CLASS).appendTo(DOM_BODY);
            self.Gallery = $('<div><div class="title"><h3></h3><button title="Close"></button></div><div class="loading" alt="Loading..."></div><div class="nav"><a class="prev" title="Previous"></a><a class="next" title="Next"></a></div><div class="canvas"><img/></div><div class="pager"></div><ul class="thumb"></ul></div>')
                .addClass(GALLERY_CLASS)
                .appendTo(DOM_BODY);

            //dom element which holds the gallery title
            self.Header = self.Gallery.find("h3:first");

            //the wrap of loading image
            self.Loading = self.Gallery.find(".loading");

            //the wrap for image of full view
            self.Canvas = self.Gallery.find(".canvas");

            //the area for showing image descripation and index information
            self.Pager = self.Gallery.find(".pager");

            //the wrap for thumbnails
            self.Thumb = self.Gallery.find(".thumb");

            //navigation triggers
            self.Nav = self.Gallery.find(".nav");
            self.Prev = self.Nav.find(".prev");
            self.Next = self.Nav.find(".next");
            self.ThumbPrev = $(HTML_LI).addClass("left").appendTo(self.Thumb);
            self.ThumbNext = $(HTML_LI).addClass("right").appendTo(self.Thumb);

            return self;
        };

        //bind dom events
        UI.prototype.bind = function () {
            var self = this;

            function close() {
                self.close();
            }

            //fill header
            self.Header.html(self.Options.GalleryName || HTML_SPACE);

            //close viewer on overlay was clicked
            self.Options.OverlayClose && self.Overlay.on(CLICK, close);

            //close viewer by clicking x on header
            self.Gallery.find(BUTTON).on(CLICK, close);

            //navigation triggers
            self.Prev.on(CLICK, function () { self.prev(); });
            self.Next.on(CLICK, function () { self.next(); });
            self.ThumbPrev.on(CLICK, function () { self.prevThumb(); });
            self.ThumbNext.on(CLICK, function () { self.nextThumb(); });

            //fill thumbnails
            $.each(self.Data, function (index, item) {
                item.Thumb.on(CLICK, function () { self.select(index); });
                $(HTML_LI).append(item.Thumb).insertBefore(self.ThumbNext);
            });

            $(window).resize(function () { self.resize(); });

            return self;
        };

        //set single view if there is only one source anchor
        UI.prototype.setView = function () {
            var self = this,
                galleryName,
                imageDesc,
                titlePieces = [];

            if (!self.IsMultiple) {
                self.Nav.hide();
                self.Thumb.hide();
                self.Pager.hide();

                galleryName = self.Options.GalleryName;
                galleryName && titlePieces.push(galleryName);

                imageDesc = self.Data[0].Desc;
                imageDesc && titlePieces.push(imageDesc);

                //gallery header = {gallery name} - {image desc}
                self.Header.html(titlePieces.join(" - ") || HTML_SPACE);
            }

            return self;
        };

        //resize gallery
        UI.prototype.resizeMultiple = function () {
            var self = this,
                viewport = $(window),
                galleryHeight = Math.min(self.Options.MaxHeight, Math.max(self.Options.MinHeight, Math.round(viewport.height() * .9))),
                galleryWidth = Math.min(self.Options.MaxWidth, Math.max(self.Options.MinWidth, Math.round(viewport.width() * .9))),
                headerHeight = 36,  //36 = padding-top + padding-bottom + height
                pagerHeight = self.Pager.height() + 10, //10 = padding-top + padding-bottom
                thumbHeight = self.Thumb.height() + 10, //10 = bottom
                galleryMarginTop = 10,
                galleryMarginBottom = 0;

            //apply gallery styles
            self.Gallery.css({
                marginTop: -Math.round(galleryHeight / 2),
                marginLeft: -Math.round(galleryWidth / 2),
                height: galleryHeight,
                width: galleryWidth
            });

            self.Canvas.height(galleryHeight - headerHeight - pagerHeight - thumbHeight - galleryMarginTop - galleryMarginBottom);

            self.Thumb.width(galleryWidth - 70); //70 = padding-left + padding-right
            self.calcThumbPages().scrollThumb();

            return self;
        };

        UI.prototype.resizeSingle = function () {
            var self = this,
                viewport = $(window),
                image = self.SelectedIndex >= 0 && self.Data[self.SelectedIndex].Image[0],
                imageWidth = image.width,
                imageHeight = image.height,
                imageRatio = imageHeight / imageWidth,
                headerHeight = 36,  //36 = padding-top + padding-bottom + height
                galleryHeight = Math.min(self.Options.MaxHeight, Math.max(self.Options.MinHeight, imageHeight + headerHeight), Math.round(viewport.height() * .9)),
                galleryWidth = Math.min(self.Options.MaxWidth, Math.max(self.Options.MinWidth, imageWidth), Math.round(viewport.width() * .9)),
                galleryRatio = (galleryHeight - headerHeight) / galleryWidth;

                if(galleryRatio < imageRatio) {
                    galleryWidth = Math.round((galleryHeight - headerHeight) / imageRatio);
                } else {
                    galleryHeight = Math.round(galleryWidth * imageRatio) + headerHeight;
                }

            //apply gallery styles
            self.Gallery.css({
                marginTop: -Math.round(galleryHeight / 2),
                marginLeft: -Math.round(galleryWidth / 2),
                height: galleryHeight,
                width: galleryWidth
            });

            self.Canvas.css({
                height: galleryHeight - headerHeight,
                marginTop: 0
            });

            return self;
        };

        UI.prototype.resize = function () {
            return this.IsMultiple ? this.resizeMultiple() : this.resizeSingle();
        };

        //set this.ThumbPages
        //  as in [{First: 0, Last: 8, Width: 100 }, {First: 9, Last: 16, Width: 100 } ... ]
        UI.prototype.calcThumbPages = function () {
            var self = this,
                pageWidthCapacity = self.Thumb.width(),
                curPageStart = 0,
                curPageWidth = 0;

            self.ThumbPages = [];
            $.each(self.Data, function (i, item) {
                var thumbWidth = item.Thumb.width() + 6; //6 = margin-left + margin-right
                if (curPageWidth + thumbWidth > pageWidthCapacity) {
                    self.ThumbPages.push({
                        First: curPageStart,
                        Last: i - 1,
                        Width: curPageWidth
                    });

                    curPageStart = i;
                    curPageWidth = thumbWidth;
                } else {
                    curPageWidth += thumbWidth;
                }
            });

            curPageWidth && self.ThumbPages.push({
                First: curPageStart,
                Last: self.Count - 1,
                Width: curPageWidth
            });

            return self;
        };

        //scrolls thumbnail to the page of current selected image
        UI.prototype.scrollThumb = function () {
            var self = this,
                index = self.SelectedIndex,
                left = 0,
                pageStart = 0;

            //determine current visible page
            $.each(self.ThumbPages || [], function (i, page) {
                if (index <= page.Last) {
                    self.SelectedThumbPage = i;
                    pageStart = page.First;
                    return false;
                }

                left += page.Width;
            });

            //Mark corresponding thumbnail as selected,
            //  and hide thumbnails of other pages
            $.each(self.Data, function (i, item) {
                item.Thumb.toggleClass(SELECTED_CLASS, i === index)
                    .css(CSS_VISIBILITY, i < pageStart ? HIDDEN : VISIBLE);
            });

            //scroll thumb
            self.ThumbPrev.next().css(CSS_MARGIN_LEFT, -left);

            return self;
        };

        //show the gallery
        UI.prototype.open = function () {
            this.Gallery.show();
            this.Overlay.show();
        };

        //hides the gallery
        UI.prototype.close = function () {
            this.Gallery.hide();
            this.Overlay.hide();
        };

        //change selected image
        UI.prototype.select = function (index) {
            var self = this,
                item,
                next,
                prev;

            self.Initialized ? self.open() : self.init();

            //Loop
            if (index < 0 || index >= self.Count) {
                if (!self.Options.Loop) return self;

                while (index < 0) index += self.Count;
                index %= self.Count;
            }

            //Cancel it if selected index hasn't been changed
            if (index === self.SelectedIndex) {
                return self;
            }

            //busy
            self.Loading.show();

            item = self.Data[index];

            function showImage(i, it) {
                self.SelectedIndex = index;
                if (self.IsMultiple) {
                    //Show image description
                    var title = [(i + 1) + " of " + self.Count];
                    it.Desc && title.push(it.Desc);
                    self.Pager.html(title.join(": "));

                    //scroll thumnails
                    self.scrollThumb();
                } else {
                    self.resizeSingle();
                }

                self.Canvas.find("img").replaceWith(it.Image);

                //idle
                self.Loading.hide();
            }

            //show selected image
            if (item.Image) {
                showImage(index, item)
            } else {
                self.preload(item.Url, function (img) {
                    item.Image = img;
                    showImage(index, item);
                });
            }

            //Preload next
            next = self.Data[index === self.Count - 1 ? 0 : index + 1];
            !next.Image && self.preload(next.Url, function (img) { next.Image = img; });

            //Preload prev
            prev = self.Data[index === 0 ? self.Count - 1 : index - 1];
            !prev.Image && self.preload(prev.Url, function (img) { prev.Image = img; });

            return self;
        };

        //navigates to previous image
        UI.prototype.prev = function () {
            this.select(this.SelectedIndex - 1);
        };

        //navigates to next image
        UI.prototype.next = function () {
            this.select(this.SelectedIndex + 1);
        };

        //navigates thumbnails to previous page
        UI.prototype.prevThumb = function () {
            //disable thumbnail page navigation if there is only one page
            if (this.ThumbPages.length < 2) return;

            var curPageFirstImage = this.ThumbPages[this.SelectedThumbPage].First;
            this.select(curPageFirstImage - 1);
        };

        //navigates thumbnails to next page
        UI.prototype.nextThumb = function () {
            //disable thumbnail page navigation if there is only one page
            if (this.ThumbPages.length < 2) return;

            var curPageLastImage = this.ThumbPages[this.SelectedThumbPage].Last;
            this.select(curPageLastImage + 1);
        };

        settings = $.extend(true, {
            //gallery name
            GalleryName: null,

            //If current image is at last, switch to the first image for "next" command; vice versa.
            Loop: true,

            //to prevent the browser from following the link,
            //  whether remove the href attribute for the anchors(<a>) after the plugin has been initialized.
            RemoveHref: true,

            //whether to close the gallery on overlay was clicked
            OverlayClose: true,

            MinWidth: 500,
            MinHeight: 313,

            MaxWidth: 1680,
            MaxHeight: 1050
        }, settings);

        //"this" is as in: [<a href="{Big_Picture_Url}" title="{Description_Of_Picture}"><img src="{Thumbnail_Url}" /></a>]
        var ui = new UI(settings, this);
        this.each(function (i, element) {
            $(element).off(CLICK).on(CLICK, function () {
                ui.select(i);

                //prevent browser from following the link
                return false;
            });
        });

        return this;
    };
} (jQuery));